AWS LambdaからAmazon EFSをマウントしてみた
AWS LambdaからAmazon EFSをマウントする機会があったのでブログに残します。
やること
今回は以下のドキュメントの手順を参考にCloudFormation 化してLambdaからEFSをマウントします。
マウント後にファイルの作成とストレージ内にファイルが作成されているか確認を行います。
簡単ですが構成図は以下のようになります。
CloudShellはEFS内のファイル確認用に使用します。
以下のブログで紹介した手順でCloudShellからEFSをマウントします。
設定
AWSリソースの作成は以下のCloudFormationテンプレートで作成を行いました。
AWSTemplateFormatVersion: "2010-09-09"
Description: Network Stack
Metadata:
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------#
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Parameters for System Name Prefix
Parameters:
- SystemPrefix
- Environment
- Label:
default: Parameters for VPC
Parameters:
- VPCCIDR
- Label:
default: Parameters for Subnet
Parameters:
- PublicSubnet01CIDR
Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
SystemPrefix:
Default: test
Type: String
Environment:
AllowedValues:
- dev
- prod
- stg
Default: dev
Type: String
VPCCIDR:
Default: 10.0.0.0/16
Type: String
PublicSubnet01CIDR:
Default: 10.0.0.0/24
Type: String
Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------#
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCIDR
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${SystemPrefix}-${Environment}-vpc
# ------------------------------------------------------------#
# InternetGateway
# ------------------------------------------------------------#
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${SystemPrefix}-${Environment}-igw
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------#
PublicSubnet01:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: ap-northeast-1a
CidrBlock: !Ref PublicSubnet01CIDR
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${SystemPrefix}-${Environment}-pub-01
VpcId: !Ref VPC
# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------#
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemPrefix}-${Environment}-public-rtb
PublicRouteTableRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
RouteTableId: !Ref PublicRouteTable
PublicRtAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet01
# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------#
EFSSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for EFS
GroupName: !Sub ${SystemPrefix}-${Environment}-efs-sg
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
SecurityGroupIngress:
- CidrIp: !Ref VPCCIDR
FromPort: 2049
IpProtocol: tcp
ToPort: 2049
Tags:
- Key: Name
Value: !Sub ${SystemPrefix}-${Environment}-efs-sg
VpcId: !Ref VPC
CloudShellSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for CloudShell
GroupName: !Sub ${SystemPrefix}-${Environment}-cloudshell-sg
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
Tags:
- Key: Name
Value: !Sub ${SystemPrefix}-${Environment}-cloudshell-sg
VpcId: !Ref VPC
LambdaSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for Lambda
GroupName: !Sub ${SystemPrefix}-${Environment}-lambda-sg
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
Tags:
- Key: Name
Value: !Sub ${SystemPrefix}-${Environment}-lambda-sg
VpcId: !Ref VPC
# ------------------------------------------------------------#
# EFS
# ------------------------------------------------------------#
EFS:
Type: AWS::EFS::FileSystem
Properties:
Encrypted: true
FileSystemTags:
- Key: Name
Value: !Sub ${SystemPrefix}-${Environment}-efs
MountTarget1:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFS
SecurityGroups:
- !Ref EFSSG
SubnetId: !Ref PublicSubnet01
AccessPoint:
Type: AWS::EFS::AccessPoint
Properties:
FileSystemId: !Ref EFS
PosixUser:
Gid: 1001
Uid: 1001
RootDirectory:
CreationInfo:
OwnerGid: 1001
OwnerUid: 1001
Permissions: 755
Path: "/efs"
# ------------------------------------------------------------#
# ElasticIP
# ------------------------------------------------------------#
EIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub ${SystemPrefix}-${Environment}-eip
# ------------------------------------------------------------#
# Lambda
# ------------------------------------------------------------#
LambdaIAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
- arn:aws:iam::aws:policy/AmazonElasticFileSystemClientReadWriteAccess
Policies:
- PolicyName: lambda-iam-policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- cloudwatch:PutMetricAlarm
- cloudwatch:DeleteAlarms
Resource: "*"
Lambda:
DependsOn: MountTarget1
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import os
import datetime
def lambda_handler(event, context):
now = datetime.datetime.now()
strnow = now.strftime('%Y-%m-%d-%H-%M-%S')
print(strnow)
filepath = f'/mnt/efs/{strnow}.txt'
with open(filepath, 'w') as f:
f.write(strnow)
print(os.listdir(os.path.join('/mnt/efs')))
FunctionName: efs-lambda
FileSystemConfigs:
- Arn: !GetAtt AccessPoint.Arn
LocalMountPath: "/mnt/efs"
Handler: index.lambda_handler
Role: !GetAtt LambdaIAMRole.Arn
Runtime: python3.12
Timeout: 5
VpcConfig:
SecurityGroupIds:
- !Ref LambdaSG
SubnetIds:
- !Ref PublicSubnet01
173~201行目でEFSを作成して218~273行目でLambdaを作成しています。
EFSではマウントターゲットの作成だけでなくアクセスポイントの作成も行っています。
また、LambdaではEFSで作成したアクセスポイントを参照しています。(262行目のFileSystemConfigsで設定)
Lambda関数のコードは以下のようになっています。
コード自体はシンプルでLambdaを実行した時間をテキストファイルのファイル名として設定してEFS内に書き込むようにしています。
import os
import datetime
def lambda_handler(event, context):
now = datetime.datetime.now()
strnow = now.strftime('%Y-%m-%d-%H-%M-%S')
print(strnow)
filepath = f'/mnt/efs/{strnow}.txt'
with open(filepath, 'w') as f:
f.write(strnow)
print(os.listdir(os.path.join('/mnt/efs')))
CloudFormationのデプロイは以下のコマンドを実行します。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --capabilities CAPABILITY_NAMED_IAM
デプロイが完了したらLambdaのコンソールから「efs-lambda」という関数を開きます。
開いたらテストタブをクリックして「テスト」をクリックします。
正常にLambdaの実行が完了するとファイル名が出力されます。
上記のログからだけでなくCloudShellからEFSをマウントしてファイルを確認してみます。
CloudShellからEFSのマウントは以下のブログを参照してください。
今回使用しているCloudFormationを正常にデプロイできていればElasticIPアドレスとセキュリティグループが作成されているのでそちらを使用してください。
CloudShellからEFSをマウントしたらlsコマンドでファイルが作成されていることを確認します。
正常に作成されていると以下のようにファイルが確認できます。
ls -la efs/efs/
total 12
drwxr-xr-x. 2 1001 1001 6144 Aug 30 12:24 .
drwxr-xr-x. 3 root root 6144 Aug 30 12:24 ..
-rw-rw-r--. 1 1001 1001 19 Aug 30 12:24 2024-08-30-12-24-32.txt
マネジメントコンソールからLambdaの設定を確認
マネジメントコンソールから設定を確認するには該当のLambdaを開いて「設定」→「ファイルシステム」で確認することができます。
編集をクリックするとEFSやアクセスポイントの設定が変更できます。
さいごに
Lambdaの/tmp領域が10GBまでしか使用できないため、それを超えるようなファイルを作成する時やEFSにファイルを元々溜めていてLambdaでバッチ処理を行いたいみたいな時に使用するのがよいと思います。